package com.rainyun.rc.ioc;

import org.jetbrains.annotations.Nullable;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**RainIOC对象管理中心*/
public class RainIOC {
	
	/**RainIOC对象枚举信息储存中心*/
	private static Map<String, BindInfo<?>> bindInfos = new HashMap<>();
	
	/**(常用)
	 * */
	public static void load(List<Class<?>> classes) {
		//新的对象枚举信息(绑定类)
		List<BindInfo<?>> new_infos = new ArrayList<BindInfo<?>>();
		//出现的静态字段
		Map<Field,String> staticFields = new HashMap<Field, String>();

		for(Class<?> c:classes) {
			String en = getIOCKey(c);
			BindInfo<?> info = null;
			if(en!=null) {
				info = getInfoOrNew(en);
				if(info.getBindClazz()!=null) {//不可重复绑定
					System.out.println("RainIOC: "+en+"is already bind to"+info.getBindClazz()+",don't be again at"+c.getName());
					continue;
				}else info.setBindClazz(c);
				
				if(info.getObj()==null&&!c.isAnnotationPresent(BeanNotInit.class)) {
					//当已存在对象时,不覆盖
					try { //新建并载入对象
						info.setObj(c.newInstance());
					} catch (InstantiationException | IllegalAccessException e) {
						e.printStackTrace();
					}
				}
				new_infos.add(info);
			}
			//添加到其他对象的被引用
			for(Field f:c.getDeclaredFields()) {
				String fen = getIOCKey(f);
				if(fen!=null) {
					f.setAccessible(true);//关掉修饰判断
					BindInfo<?> feninfo = getInfoOrNew(fen);
					if(Modifier.isStatic(f.getModifiers())) {
						feninfo.getReferedStaticFields().put(c,f);
						staticFields.put(f,fen);
					}else if(info!=null){
						feninfo.getReferedFields().put(en,f);
						info.getReferFields().put(f,fen);
					}else {
						System.out.println("RainIOC: cannot effect at "+c.getName()+"."+f.getName()+" because itsn't static or class isn't Bean");
					}
				}
			}
		}
		//载入完成后
		//同步自身到其他+同步其他到自身
		for(BindInfo<?> info:new_infos) {
			syncToOther(info);//自身到其他
			syncFromOther(info);//其他到自身
		}
		//设置所有静态字段
		for(Entry<Field, String> en : staticFields.entrySet()) {
			try {
				en.getKey().set(null,get(en.getValue()));
			} catch (IllegalArgumentException | IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		//初始化
		for(BindInfo<?> info:new_infos){
			Object obj = info.getObj();
			if(obj!=null&&obj instanceof BeanPostInit)
				((BeanPostInit)obj).init();
		}
	}
	
	/**(常用)卸载*/
	public static void unload(List<Class<?>> classes) {
		List<BindInfo<?>> unload_infos = new ArrayList<BindInfo<?>>();
		List<Class<?>> unload_enums = new ArrayList<>();//需要卸载的枚举主类
		for(Class<?> c:classes) {
			if(c.isAnnotation()&&c.isAnnotationPresent(BeanAnno.class)) {
				try {
					for(Method val:c.getDeclaredMethods()){
						if(val.isAnnotationPresent(BeanKey.class)){
							val.setAccessible(true);
							unload_enums.add(val.getReturnType());
							break;
						}
					}
				} catch (SecurityException e) {
					e.printStackTrace();
				}
				continue;
			}
			
			String en = getIOCKey(c);
			if(en!=null) {
				BindInfo<?> info = getInfoOrNew(en);
				//设置所有信息为null(哪怕被外部实现,都应该清理掉)
				info.setObj(null);
				//取消自身所有引用
				info.getReferFields().clear();
				unload_infos.add(info);
			}
			//移除其他枚举的该类引用
			for(Field f:c.getDeclaredFields()) {
				String fen = getIOCKey(f);
				if(fen!=null) {
					BindInfo<?> feninfo = getInfoOrNew(fen);
					if(Modifier.isStatic(f.getModifiers())) {//移除静态
						feninfo.getReferedStaticFields().remove(c);
					}else if(en!=null){
						feninfo.getReferedFields().remove(en);
					}
				}
			}
		}
		
		//同步自己到其他(设置null)
		for(BindInfo<?> info:unload_infos) {
			syncToOther(info);
		}
		for(Class<?> unen:unload_enums) {//卸载对象名,清空所有值,删除所有关联
			Field[] fs = unen.getDeclaredFields();
			for(Field f:fs) {
				f.setAccessible(true);
				try {
					Object en = f.get(null);
					if(en instanceof Enum<?>) {
						BindInfo<?> info = bindInfos.get((Enum<?>) en);
						if(info!=null) {
							info.setObj(null);
							syncToOther(info);
							bindInfos.remove(en);
						}
						for(Entry<String, BindInfo<?>> en1: bindInfos.entrySet()) {
							BindInfo<?> info1 = en1.getValue();
							info1.getReferedFields().remove(en);
						}
					}
				} catch (IllegalArgumentException | IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**获取或新建*/
	public static BindInfo<?> getInfoOrNew(Enum<?> en) {
		return getInfoOrNew(enumToKey(en));
	}

	/**(常用)获取或新建
	 * @param key 对象名称*/
	public static BindInfo<?> getInfoOrNew(String key) {
		BindInfo<?> info = bindInfos.get(key);
		if(info==null) {
			info = new BindInfo(key);
			bindInfos.put(key,info);
		}
		return info;
	}

	/**(常用)获取元素(类,字段所绑定)的对象名*/
	public static String getIOCKey(AnnotatedElement ele) {
		Beanzy bzy = ele.getDeclaredAnnotation(Beanzy.class);
		if(bzy!=null){ //使用Beanzy
			String key = bzy.value();
			if(key.isEmpty()){//类为键
				if(ele instanceof Field){
					return ((Field)ele).getType().getName();
				}else if(ele instanceof Class){
					return ((Class)ele).getName();
				}
			}else return key;//字符串为键
		}

		//使用自定义枚举
		Class<? extends Annotation> type;
		for(Annotation an:ele.getAnnotations()) {
			type = an.annotationType();
			if(type.isAnnotationPresent(BeanAnno.class)) {
				try {
					Method val = null;
					for(Method m:type.getDeclaredMethods())
						if (m.isAnnotationPresent(BeanKey.class))
						val = m;
					if(val==null){//类为键
						return ((Class)ele).getName();
					}
					val.setAccessible(true);
					Object value = val.invoke(an);
					if(value instanceof Enum<?>){
						return enumToKey((Enum<?>)value);//枚举为键
					}else {
						String str = value.toString();
						return str.isEmpty()?((Class)ele).getName():value.toString();
						//字符串或其他为键,为空时类为键
					}
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (SecurityException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	public static String enumToKey(Enum<?> en){
		return en.name()+":"+en.getDeclaringClass().getName();
	}

	/**(常用为复制局部变量)获取单例类对应的对象*/
	public static <T> T get(Class<T> en) {
		return get(en.getName());
	}
	/**(常用为复制局部变量)获取枚举对应的对象*/
	public static <T> T get(Enum<?> en) {
		return get(enumToKey(en));
	}
	/**(常用为复制局部变量)获取名称对应的对象*/
	public static <T> T get(String key) {
		BindInfo<T> info = (BindInfo<T>) bindInfos.get(key);
		if(info!=null)
			return info.getObj();
		return null;
	}

	/**(常用)存入单例对应的对象,并瞬间同步*/
	public static void set(Class<?> c,Object obj) {
		set(c.getName(),obj);
	}
	/**(常用)存入单例对应的对象,并瞬间同步*/
	public static void set(Object obj) {
		set(obj.getClass().getName(),obj);
	}
	/**(常用)存入枚举对应的对象,并瞬间同步*/
	public static void set(Enum<?> en,Object obj) {
		set(enumToKey(en),obj);
	}
	/**(常用)存入名称对应的对象,并瞬间同步*/
	public static void set(String key,Object obj) {
		BindInfo<?> info = getInfoOrNew(key);
		info.setObj(obj);
		syncToOther(info);
		syncFromOther(info);
	}
	
	/**同步赋值所有枚举对象到所有被引用字段*/
	public static void syncToOther(BindInfo<?> info) {
		//同步对象字段
		for(Entry<String, Field> en1:info.getReferedFields().entrySet()) {
			BindInfo<?> tar_info = bindInfos.get(en1.getKey());
			if(tar_info==null)continue;//排除空
			Object tar_obj = tar_info.getObj();
			if(tar_obj==null)continue;//排除空
			try {
				en1.getValue().set(tar_obj,info.getObj());
			} catch (IllegalArgumentException | IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		//同步静态字段
		for(Entry<Class<?>, Field> en1:info.getReferedStaticFields().entrySet()) {
			try {
				en1.getValue().set(null,info.getObj());
			} catch (IllegalArgumentException | IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**同步其他字段到自身枚举对象字段*/
	public static void syncFromOther(BindInfo<?> info) {
		Object obj = info.getObj();
		if(obj!=null)
		for(Entry<Field, String> en:info.getReferFields().entrySet()) {
			try {
				en.getKey().set(obj,get(en.getValue()));
			} catch (IllegalArgumentException | IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}
}





